#!/usr/bin/python
# -*- coding: utf-8 -*-
# @version        : 1.0
# @Create Time    : 2024/8/6 10:03:49
# @File           : crud.py
# @IDE            : PyCharm
# @desc           : 任务池 - 增删改查
from datetime import datetime
import os
from pathlib import Path
import shutil
from typing import List, Any

from fastapi import Request, UploadFile
from sqlalchemy import not_
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm.strategy_options import _AbstractLoad, joinedload

from core.crud import DalBase
from core.exception import CustomException
from core.logger import logger
from . import models, schemas
from apps.dispatch.assignment.schemas import TaskAssignUsers
from apps.dispatch.assignment.crud import TaskAssignUsersDal
from apps.dispatch.team.crud import TaskWorkTeamDal
from apps.dispatch.team.schemas import TaskWorkTeam
from apps.dispatch.need.crud import TaskNeedDal
from apps.dispatch.need.schemas import TaskNeed
from apps.base.external.crud import ExternDal
from apps.base.mock.dispatch.crud import GenerateMockData
from apps.dispatch.rule.crud import RuleDal
from application.settings import BASE_DIR, CONFIG, STATIC_ROOT, STATIC_URL
from utils.excel.excel_manage import ExcelManage


class ResourceDal:
    async def get_resources(self):
        """获取当前资源情况，包括物料，人员，设备，工装等资源
        """
        # todo 对接现场资源情况接口
        return []


class TaskPoolDal(DalBase):
    def __init__(self, db: AsyncSession):
        super(TaskPoolDal, self).__init__()
        self.db = db
        self.model = models.DCTask
        self.schema = schemas.TaskSimpleOut

    async def get_status_options(self):
        return [{'value': i} for i in CONFIG.DISPATCH.TASK_STATUS_LIST]

    async def fetch_tasks(self, request=None, **kwargs):
        """对接工单接口，同步工单，需提供以下字段
            product_type: str  # 机型
            flight: str  # 架次
            station: str  # 工位
            sub_station: str = None  # 二级工位
            position: str = None  # 站位
            type: str  # AO类型，AAO、DISR、FRR、AO等
            start: DatetimeStr = None  # 计划开工时间
            needs: dict = {}  # AO资源需求
        """
        flags = {
            'add': 0,
            'update': 0,
            'error': 0
        }
        headers=request.headers if request else None
        async for resp in ExternDal().get_ticket_list(headers=headers, **kwargs):
            data=schemas.TaskIn(**resp)
            logger.debug(f"同步工单 {data.ticket_id}")
            try:
                flag = await self.sync_task(data, headers=headers)
            except Exception as e:
                logger.error(f"同步工单 {data.ticket_id} 失败: {str(e)}")
                flags['error'] += 1
                # raise CustomException(f"同步工单 {data.ticket_id} 失败：{e}")
            else:
                if flag: flags[flag] += 1
        return f"新增 {flags['add']} 个工单, 更新 {flags['update']} 个工单, 失败 {flags['error']} 个工单"
        
    async def _get_add_info(self, ticket_id, headers=None):
        info = {}
        try:
            async for res in ExternDal().get_ticket_detail(ticket_id=ticket_id, headers=headers):
                info.update({key: value for key, value in res.items() if value is not None})
        except Exception as e:
            logger.exception(f"获取 {ticket_id} 工单详情接口相关信息失败：{e}")
            raise Exception(f"获取工单详情失败, {e}")

        rule = await RuleDal(self.db).get_datas(limit=0)
        try:
            parser = parse.TicketParser(ticket=info, rule=rule)
            info.update({key: value for key, value in parser.dumps().items() if value is not None})
        except Exception as e:
            logger.exception(f"解析 {ticket_id} 工单辅料和外协班组信息失败：{e}")
            raise Exception(f"解析工单辅料和外协失败, {e}")
        return info


    async def _get_update_info(self, ticket_id, product_type, flight, headers=None):
        info = {}
        try:
            async for data in ExternDal().get_delivery_message_and_status(ticket_id=ticket_id, headers=headers):
                info.update({key: value for key, value in data.items() if value is not None})
        except Exception as e:
            logger.exception(f"获取 {ticket_id} 物料接口相关信息失败：{e}")
            raise Exception(f"获取物料失败, {e}")
        try:
            async for data in ExternDal().get_week_and_day_plan(product_type=product_type, flight=flight, ticket_id=ticket_id, headers=headers):
                info.update({key: value for key, value in data.items() if value is not None})
        except Exception as e:
            logger.exception(f"获取 {ticket_id} 计划接口相关信息失败：{e}")
            raise Exception(f"获取计划失败, {e}")
        return info


    async def sync_task(
            self,
            data: schemas.TaskIn,
            headers = None
    ):  
        flag = None
        unique = await self.get_data(ticket_id=data.ticket_id, v_return_none=True)
        info = data.model_dump()
        if not unique:
            flag = "add"
            data = await self.create_data(data)

            info.update(await self._get_add_info(ticket_id=data.ticket_id, headers=headers))
            info.update(await self._get_update_info(ticket_id=data.ticket_id, product_type=data.product_type, flight=data.flight, headers=headers))
            
            info["create_user"] = "system"

            await self.put_data(data.id, schemas.TaskIn(**info))

            if info.get("work_teams"):
                await TaskWorkTeamDal(self.db).create_datas(datas=[TaskWorkTeam(**i, ticket_id=data.ticket_id, create_user="system") for i in info.get("work_teams")])
            
            if info.get("supplementary_needs"):
                await TaskNeedDal(self.db).create_datas(datas=[TaskNeed(**i, ticket_id=data.ticket_id, create_user="system") for i in info.get("supplementary_needs")])
                
        else:
            info.update(await self._get_update_info(ticket_id=data.ticket_id, product_type=data.product_type, flight=data.flight, headers=headers))
            schema = schemas.TaskAutoUpdate(**info)
            for key, value in schema.model_dump().items():
                if value is None:
                    continue
                if value == getattr(unique, key):
                    continue
                if key in ["start", "end"]:
                    if str(value) == str(getattr(unique, key)):
                        continue
                # if isinstance(value, bool) and key != "pi":
                #     setattr(unique, key, value)
                #     flag = "update"
                #     continue
                setattr(unique, key, value)
                flag = "update"
            if flag == "update":
                unique.update_user = "system"
                await self.flush(unique)
        return flag

    async def create_data(
            self,
            data: schemas.TaskIn,
            v_options: List[_AbstractLoad] = None
    ):
        unique = await self.get_data(ticket_id=data.ticket_id, v_return_none=True)
        if unique:
            raise CustomException(msg="记录已存在")
        
        v_options=[
            joinedload(models.DCTask.assign_users)
        ]
            
        history_ao = await self.get_data(
            ao_id=data.ao_id,
            v_return_none=True,
            v_options=v_options
        )
        if history_ao:
            data.group1 = history_ao.group1
            data.group2 = history_ao.group2
            data.tag = history_ao.tag
            data.order = history_ao.order
            data.out_team = history_ao.out_team

        data = await super().create_data(data=data, v_return_obj=True, v_options=v_options)

        if history_ao and history_ao.assign_users:
            datas = []
            for user in history_ao.assign_users:
                datas.append(TaskAssignUsers(
                    ticket_id=data.ticket_id,
                    team=user.team,
                    userid=user.userid,
                    username=user.username,
                    remark=user.remark
                ))
            datas = await TaskAssignUsersDal(self.db).create_datas(datas=datas)
        return data

    async def download_import_template(self):
        """
        下载导入模板
        """
        path = STATIC_ROOT / 'dispatch' / '任务导入模板.xlsx'
        if not path.exists():
            src = BASE_DIR / 'scripts' / 'initialize' / 'data' / '任务导入模板.xlsx'
            if not src.exists():
                raise CustomException("模板文件不存在")
            if not path.parent.exists():
                path.parent.mkdir(parents=True)
            shutil.copy(src, path)
        return {"url": str(STATIC_URL) + str(path).replace(str(STATIC_ROOT), '').replace("\\", '/'), "filename": "任务导入模板.xlsx"}

    async def get_template_data(self):
        datas = []
        for i, file in enumerate(Path(STATIC_ROOT / 'dispatch' / 'taskpool').rglob('*.xlsx')):
            data = {
                'id': i,
                'name': file.name,
                'path': str(file),
                'size': file.stat().st_size,
                'ctime': datetime.fromtimestamp(file.stat().st_ctime).strftime("%Y-%m-%d %H:%M:%S"),
                'detail': {}
            }
            gm = GenerateMockData(file=file)
            for table in ["工单列表", "工单工序", "班组列表", "班组用户", "物流配送通知", "物流配送状态", "周计划列表", "日计划列表"]:
                _, count = await gm.generate_dispatch_mock_data(table)
                data['detail'][table] = count
            datas.append(data)
        return datas, len(datas)
    

    async def get_tasks(self, **kwargs):
        v_where = []
        if kwargs.get("is_not_assign"):
            v_where.append(not_(models.DCTask.assign_users.any()))      

            del kwargs["is_not_assign"]

        datas = await self.get_datas(
            **kwargs,
            v_where=v_where,
            v_options=[
                joinedload(models.DCTask.feedback),
                joinedload(models.DCTask.assign_users),
                joinedload(models.DCTask.work_teams),
                joinedload(models.DCTask.needs),
            ],
            v_schema=schemas.TaskOut
        )
        return datas

    def _to_excel_list(self, datas, header_list: List = None):
        rows = []
        for data in datas:
            if hasattr(data, 'is_delete') and getattr(data, "is_delete"):
                continue
            row = []
            if header_list:
                for item in header_list:
                    field = item.get("field")
                    value = getattr(data, field, "")
                    row.append(value)
            else:
                row = list([v for k, v in data.model_dump().items() if k not in ['is_delete']])
            rows.append(row)
        return rows

    async def export_query_list(self, header_list: List, **kwargs):
        kwargs['limit'] = 0
        datas = await self.get_tasks(
            **kwargs,
            v_return_count=False,
            v_return_objs=True,
        )
        if not header_list:
            header_list = [
                {"label": "工单号", "field": "ticket_id"},
                {"label": "AO编号", "field": "ao_id"},
                {"label": "AO类型", "field": "ao_type"},
                {"label": "AO名称", "field": "ao_name"},
                {"label": "AO状态", "field": "status"},
                {"label": "机型", "field": "product_type"},
                {"label": "架次", "field": "flight"},
                {"label": "工位", "field": "station"},
                {"label": "二级工位", "field": "sub_station"},
                {"label": "站位", "field": "position"},
                {"label": "班组", "field": "team"},
                {"label": "班组长", "field": "team_leader"},
                {"label": "日计划", "field": "in_daily_plan"},
                {"label": "周计划", "field": "in_weekly_plan"},
                {"label": "物料配套", "field": "material_match"},
                {"label": "配送通知", "field": "delivery_message"},
                {"label": "质保标注", "field": "pi"},
                {"label": "计划开始日期", "field": "plan_start"},
                {"label": "预计归档日期", "field": "plan_end"},
                {"label": "实际开工时间", "field": "start"},
                {"label": "实际归档日期", "field": "end"},
                {"label": "标签", "field": "group1"},
                {"label": "排序", "field": "order"},
                {"label": "派工人员", "field": "assign_users"},
                {"label": "反馈问题", "field": "feedback"},
                {"label": "辅料信息", "field": "needs"},
                {"label": "外协信息", "field": "work_teams"},
                {"label": "创建时间", "field": "create_datetime"}
            ]
        header = [x['label'] for x in header_list]
        tasks, feebacks, assignments, work_teams, needs = [], [], [], [], []

        feedback_header_list = [
            {"label": "问题类别", "field": "title"},
            {"label": "问题内容", "field": "content"},
            {"label": "备注", "field": "remark"},
            {"label": "创建人", "field": "create_user"},
            {"label": "创建时间", "field": "create_datetime"},
        ]
        assignment_header_list = [
            {"label": "班组", "field": "team"},
            {"label": "工号", "field": "userid"},
            {"label": "姓名", "field": "username"},
            {"label": "创建人", "field": "create_user"},
            {"label": "创建时间", "field": "create_datetime"},
        ]
        team_header_list = [
            {"label": "外协部门", "field": "name"},
            {"label": "工序号", "field": "work_no"},
            {"label": "工序标题", "field": "work_title"},
            {"label": "工序开始时间", "field": "work_start"},
            {"label": "备注", "field": "remark"},
            {"label": "创建人", "field": "create_user"},
            {"label": "创建时间", "field": "create_datetime"},
        ]
        need_header_list = [
            {"label": "辅料名称", "field": "name"},
            {"label": "辅料类型", "field": "type"},
            {"label": "数量", "field": "number"},
            {"label": "单位", "field": "unit"},
            {"label": "备注", "field": "remark"},
            {"label": "创建人", "field": "create_user"},
            {"label": "创建时间", "field": "create_datetime"},
        ]
        for data in datas:
            data = schemas.TaskOut.model_validate(data)
            row = []
            for item in header_list:
                field = item.get("field")
                value = getattr(data, field)
                if field == "feedback":
                    feebacks.extend(self._to_excel_list(value, feedback_header_list))
                    value = '\n'.join([f'{fb.title}:{fb.content}' for fb in value])
                elif field == "assign_users":
                    assignments.extend(self._to_excel_list(value, assignment_header_list))
                    value = '\n'.join([f'{u.username}(u.userid)' for u in value])
                elif field == "work_teams":
                    work_teams.extend(self._to_excel_list(value, team_header_list))
                    value = '\n'.join(set([wt.name for wt in value]))
                elif field == "needs":
                    needs.extend(self._to_excel_list(value, need_header_list))
                    value = '\n'.join(set([n.name for n in value]))
                if value is None:
                    value = ""
                row.append(value)
            tasks.append(row)

        em = ExcelManage()
        em.create_excel("任务列表")
        em.write_list(rows=tasks, header=header)
        em.create_sheet("反馈问题")
        em.write_list(rows=feebacks, header=[x['label'] for x in feedback_header_list])
        em.create_sheet("派工人员")
        em.write_list(rows=assignments, header=[x['label'] for x in assignment_header_list])
        em.create_sheet("外协信息")
        em.write_list(rows=work_teams, header=[x['label'] for x in team_header_list])
        em.create_sheet("辅料信息")
        em.write_list(rows=needs, header=[x['label'] for x in need_header_list])
        file_url = em.save_excel()
        em.close()
        return {"url": file_url, "filename": "任务列表.xlsx"}
    